iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
Software Development

iOS 學習筆記系列 第 16

Day16 Combine 03 - Subscriber

  • 分享至 

  • xImage
  •  

Subscriber

與Publisher 相對應,是觀察者模式中的Observer,Publisher在自身狀態改變時,調用Subscriber 的三個不同方法receive(subscription)receive(_:Input)receive(completion:)來通知Subscriber

Subscriber 訂閱Publisher 後會返回一個遵循Cancellable協定的AnyCancellable,作用上類似於其他響應式框架中的dispose,控制者訂閱者的釋放,在開發中可以將其作為屬性欄位,在頁面銷毀時,調用AnyCancellable內部的cancel()方法進行資源釋放

以下舉一些常見的Subscriber 例子:

  1. sink

    Sink是非常通用的Subscriber,我們可以自由的處理數據流的狀態,Publisher 還提供了extension funcsink(receiveCompletion:, receiveValue:)方法來直接訂閱,為訂閱者連結基於閉包的行為

    let integers = (0...3)
    integers.publisher
        .sink { print("Received \($0)") }
    

    參數:

    • receiveComplete: 在完成時執行的閉包
    • receiveValue: 在收到值時執行的閉包
    let integers = (0...3)
    integers.publisher
        .sink(receiveCompletion:{print("Completion: \($0)")},
        receiveValue:{ print("Value: \($0)")})
    
  2. Subject

    Subject 是一種 Publisher,你可以調用其 send(_:) 方法將值注入到數據流中,很適合將已有的程式改造成兼容 Combine,有些時候我們想隨時在Publisher 插入值來通知訂閱者,在Rx 中也提供了一個Subject類型來實現。Subject 通常是一個中間代理,即可以作為Publisher,也可以作為Observer

    • 作為Observer的時候,可以通過Publisher的subscribe(_:Subject)方法訂閱某個Publisher
    • 作為Publisher的時候,可以主動通過Subject的兩個send方法,我們可以在數據流中隨時插入數據

    目前在Combine 中,有三個已經實現對Subject: AnySubjectCurrentValueSubjectPassthroughSubject

    範例:

    // Before
    class ContentManager {
        var content: [String] {
            didSet {
                delegate?.contentDidChange(content)
            }
        }
        func getContent() {
            content = ["hello", "world"]
        }
    }
    
    // After
    class ContentController {
        var content = CurrentValueSubject<[String], NSError>([])
        func getContent() {
            content.value = ["hello", "world"]
        }
    }
    

    CurrentValueSubject的功能很簡單,就是包含一個初始值,並且會在每次值變化的時候發送一個消息,這個值會被保存,可以很方便的用來替代Property Observer。在上例中,以前需要實現delegate 來獲取變化,現在只需要訂閱content 的變化即可,並且它作為一個Publisher,可以很方便的利用操作符進行組合變換

    PassthroughSubjectCurrentValueSubject幾乎一樣,只是沒有初始值,也不會保存任何值

  3. Assign

    Assign可以很方便地將接收到的值通過KeyPath設置到指定的Class 上(不支持Struct),很適合將已有的程式改造成Reactive,將來自Publisher 的每個元素分配給物件中的屬性

    參數:

    • keyPath:表示要分配屬性的 keyPath。關於 Key-Path 表達式請參考此官方文章
    • object:包含此屬性的物件。訂閱者在每次接收到新值時都會分配給該物件的屬性
    class Student {
        let name: String
        var score: Int
    
        init(name: String, score: Int) {
            self.name = name
            self.score = score
        }
    }
    
    let student = Student(name: "Ryder", score: 91)
    print(student.score)
    let observer = Subscribers.Assign(object: student, keyPath: \Student.score)
    let publisher = PassthroughSubject<Int, Never>()
    publisher.subscribe(observer)
    publisher.send(95)
    print(student.score)
    publisher.send(98)
    print(student.score)
    

    一旦publisher 的值發生改變,對應的studentscore也會被更新


上一篇
Day15 Combine 02 - Publisher
下一篇
Day17 Combine 04 - Operators 主要類型
系列文
iOS 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言